/*
 * VHBaseDataServiceImpl.java
 * Created on 24 févr. 2009
 *
 * Glacéo Internet Platform
 * http://sourceforge.net/projects/chm/
 *
 * Copyright (c) 2005-2009, Jens and Jan Janke (VirtualHockey Project)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
 */

package com.hockeo.vhweb.server.servlet;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.extjs.gxt.ui.client.Style.SortDir;
import com.extjs.gxt.ui.client.data.BasePagingLoadResult;
import com.extjs.gxt.ui.client.data.PagingLoadConfig;
import com.extjs.gxt.ui.client.data.PagingLoadResult;
import com.hockeo.vhbase.conf.GRulesConstants;
import com.hockeo.vhbase.data.dao.assoc.GInternationalAssociation;
import com.hockeo.vhbase.data.dao.assoc.GNationalAssociation;
import com.hockeo.vhbase.data.dao.club.GClub;
import com.hockeo.vhbase.data.dao.club.GClubAbstractIntCupHistoryRecord;
import com.hockeo.vhbase.data.dao.club.GClubCLHistoryRecord;
import com.hockeo.vhbase.data.dao.club.GClubCupHistoryRecord;
import com.hockeo.vhbase.data.dao.club.GClubHistoryRecord;
import com.hockeo.vhbase.data.dao.club.GClubLeagueHistoryRecord;
import com.hockeo.vhbase.data.dao.club.GClubWLHistoryRecord;
import com.hockeo.vhbase.data.dao.contest.GContest;
import com.hockeo.vhbase.data.dao.contest.GContestElement;
import com.hockeo.vhbase.data.dao.contest.GContestRound;
import com.hockeo.vhbase.data.dao.contest.GKO3ContestElement;
import com.hockeo.vhbase.data.dao.contest.GLeague;
import com.hockeo.vhbase.data.dao.contest.GNationalCup;
import com.hockeo.vhbase.data.dao.contest.GRoundRobinContestElement;
import com.hockeo.vhbase.data.dao.match.GMatch;
import com.hockeo.vhbase.data.dao.season.GMatchday;
import com.hockeo.vhbase.data.dao.season.GSeason;
import com.hockeo.vhbase.data.dao.standings.GArchivedStandings;
import com.hockeo.vhbase.data.dao.standings.GECRStandings;
import com.hockeo.vhbase.data.dao.standings.GENRStandings;
import com.hockeo.vhbase.data.dao.standings.GStandings;
import com.hockeo.vhbase.data.helper.GArchivedStandingsType;
import com.hockeo.vhbase.data.helper.GContestRoundFormat;
import com.hockeo.vhbase.data.helper.GMatchType;
import com.hockeo.vhbase.data.helper.GSortDir;
import com.hockeo.vhbase.data.helper.GStandingsType;
import com.hockeo.vhbase.error.GDataException;
import com.hockeo.vhbase.error.GDataValidationException;
import com.hockeo.vhbase.error.GException;
import com.hockeo.vhweb.client.rpc.VHBaseDataService;
import com.hockeo.vhweb.client.utils.VHJsData;
import com.hockeo.vhweb.client.utils.VHServerException;
import com.hockeo.vhweb.client.utils.enums.VHJsClubContestHistoryType;
import com.hockeo.vhweb.client.utils.enums.VHJsStandingsType;
import com.hockeo.vhweb.server.servlet.base.VHAbstractService;

/**
 * Implementation of the {@link VHBaseDataService} interface.
 *
 * @version $Id$
 * @author jjanke
 */
public class VHBaseDataServiceImpl extends VHAbstractService implements VHBaseDataService
{
  public List<VHJsData> getClubContestHistoryRecords( String strLocale, String strContestId, Integer nSeasonId )
      throws VHServerException
  {
    GSeason season;
    List<GClubHistoryRecord> list;
    GContest contest = getDao().findEntity( GContest.class, strContestId );

    if ( nSeasonId == null )
    {
      try
      {
        season = getDao().findCurrentSeason();
      }
      catch ( GDataException ex )
      {
        throw new VHServerException( "No season is marked as being active.", ex );
      }
    }
    else
      season = getDao().findEntity( GSeason.class, nSeasonId );

    list = getDao().findClubHistoryRecords( GClubHistoryRecord.class, contest, season );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getClubContestHistoryRecords( String strLocale, String strClubId,
      VHJsClubContestHistoryType type ) throws VHServerException
  {
    List<? extends GClubHistoryRecord> list;
    GClub club = getDao().findEntity( GClub.class, strClubId );

    switch ( type )
    {
      case NATIONAL_LEAGUE:
        list = getDao().findClubHistoryRecords( GClubLeagueHistoryRecord.class, club );
        break;

      case NATIONAL_CUP:
        list = getDao().findClubHistoryRecords( GClubCupHistoryRecord.class, club );
        break;

      case INTERNATIONAL_CUP:
        list = getDao().findClubHistoryRecords( GClubAbstractIntCupHistoryRecord.class, club );
        break;

      default:
        throw new VHServerException( "The club/contest history type '" + type.name() + "' is not supported." );
    }

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public VHJsData getNationalAssociation( String strLocale, String strAssocId ) throws VHServerException
  {
    GNationalAssociation assoc = getDao().findEntity( GNationalAssociation.class, strAssocId );

    return getDaoConverter().convert( assoc, resolveLocale( strLocale ) );
  }

  public VHJsData getClub( String strLocale, String strClubId ) throws VHServerException
  {
    GClub club = getDao().findEntity( GClub.class, strClubId );

    return getDaoConverter().convert( club, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getClubsForLeague( String strLocale, String strLeagueId ) throws VHServerException
  {
    GLeague league = getDao().findEntity( GLeague.class, strLeagueId );

    return getDaoConverter().convert( league.getClubs(), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getClubSeasonSummaries( String strLocale, String strClubId ) throws VHServerException
  {
    GClub club = getDao().findEntity( GClub.class, strClubId );

    return getDaoConverter().convert( getDao().findClubSeasonSummaries( club ), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getContestHistoryRecords( String strLocale, String strContestId ) throws VHServerException
  {
    GContest contest = getDao().findEntity( GContest.class, strContestId );

    return getDaoConverter().convert( getDao().findContestHistoryRecords( contest ), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getAllMajorContestHistoryRecords( String strLocale, Integer nSeasonId ) throws VHServerException
  {
    GSeason season = null;

    if ( nSeasonId == null )
    {
      try
      {
        season = getDao().findCurrentSeason();
      }
      catch ( GDataException ex )
      {
        throw new VHServerException( "There is currently no active season.", ex );
      }
    }
    else
      season = getDao().findEntity( GSeason.class, nSeasonId );

    return getDaoConverter().convert( getDao().findMajorContestHistoryRecords( season ), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getCountrySeasonOverviewRecords( String strLocale, String strAssocId, Integer nSeasonId )
      throws VHServerException
  {
    List<VHJsData> list;
    GNationalAssociation assoc = getDao().findEntity( GNationalAssociation.class, strAssocId );
    GSeason season = null;
    Locale locale = resolveLocale( strLocale );

    if ( nSeasonId == null )
    {
      try
      {
        season = getDao().findCurrentSeason();
      }
      catch ( GDataException ex )
      {
        throw new VHServerException( "There is currently no active season.", ex );
      }
    }
    else
      season = getDao().findEntity( GSeason.class, nSeasonId );

    // Club/cup history objects
    //
    list = getDaoConverter().convert(
        getDao().findClubHistoryRecordsByRank( getDao().findEntity( GNationalCup.class, assoc.getNationalCupId() ),
            season, 1, 3 ), locale );

    // Club/league history objects
    //
    list.addAll( getDaoConverter().convert( getDao().findSpecialClubLeagueHistoryRecords( season, assoc ), locale ) );

    // Club/int cup history objects
    //
    list.addAll( getDaoConverter().convert(
        getDao().findClubHistoryRecords( GClubAbstractIntCupHistoryRecord.class, assoc, season ), locale ) );

    return list;
  }

  public List<VHJsData> getContestElements( String strLocale, String strRoundId ) throws VHServerException
  {
    GContestRound round = getDao().findEntity( GContestRound.class, strRoundId );

    return getDaoConverter().convert( round.getContestElements(), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getContestRounds( String strLocale, String strContestId ) throws VHServerException
  {
    GContest contest = getDao().findEntity( GContest.class, strContestId );

    return getDaoConverter().convert( contest.getContestRounds(), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getCountries( String strLocale ) throws VHServerException
  {
    List<GNationalAssociation> list = getDao().findAllEntities( GNationalAssociation.class );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public Map<String, VHJsData> getCurrentClubSituation( String strLocale, String strClubId ) throws VHServerException
  {
    HashMap<String, VHJsData> map = new HashMap<String, VHJsData>();
    GClub club = getDao().findEntity( GClub.class, strClubId );
    GSeason season;

    try
    {
      season = getDao().findCurrentSeason();
    }
    catch ( GDataException ex1 )
    {
      throw new VHServerException( "There is currently no active season." );
    }

    try
    {
      GStandings standings = getDao().findCurrentStandings( GStandingsType.TOTAL, club,
          club.getLeague().getRegularSeasonElement() );

      VHJsData data = getDaoConverter().convert( standings, resolveLocale( strLocale ) );

      // add some additional data
      //
      data.set( "numTotalMatches", club.getLeague().getNumMatchesPerClub() );
      data.set( "numTotalMatchdays", club.getLeague().getNumMatchdays() );

      map.put( "league", data );
    }
    catch ( GDataException ex )
    {
      // No specific handling, it just means that no matchday exists for the given contest
      // element
      //
    }

    GClubCupHistoryRecord cupHistory = getDao().findClubHistoryRecord( GClubCupHistoryRecord.class,
        getDao().findEntity( GNationalCup.class, club.getAssociation().getNationalCupId() ), season, club );
    if ( cupHistory != null )
      map.put( "cup", getDaoConverter().convert( cupHistory, resolveLocale( strLocale ) ) );

    GClubCLHistoryRecord clHistory = getDao().findClubHistoryRecord( GClubCLHistoryRecord.class, season, club );
    if ( clHistory != null )
      map.put( "cl", getDaoConverter().convert( clHistory, resolveLocale( strLocale ) ) );

    GClubWLHistoryRecord wlHistory = getDao().findClubHistoryRecord( GClubWLHistoryRecord.class, season, club );
    if ( wlHistory != null )
      map.put( "wl", getDaoConverter().convert( wlHistory, resolveLocale( strLocale ) ) );

    GECRStandings ecr = getDao().findECRStandings( club );
    if ( ecr != null )
      map.put( "ecr", getDaoConverter().convert( ecr, resolveLocale( strLocale ) ) );

    return map;
  }

  public PagingLoadResult<VHJsData> getEternalStandings( PagingLoadConfig cfg, String strLocale, String strElementId )
      throws VHServerException
  {
    GSortDir sortDir = GSortDir.DEFAULT;

    if ( cfg.getSortDir() != null && cfg.getSortDir() != SortDir.NONE )
      sortDir = GSortDir.valueOf( cfg.getSortDir().name() );

    GContestElement element = getDao().findEntity( GContestElement.class, strElementId );
    List<GArchivedStandings> list = getDao().findEternalStandings( element, cfg.getSortField(), sortDir,
        cfg.getOffset(), cfg.getLimit() );

    return new BasePagingLoadResult<VHJsData>( getDaoConverter().convert( list, resolveLocale( strLocale ) ),
        cfg.getOffset(), (int) getDao().findNumArchivedStandings( GArchivedStandingsType.ETERNAL, null, element ) );
  }

  public List<VHJsData> getENRStandings( String strLocale, Integer nSeasonId ) throws VHServerException
  {
    GSeason season = null;

    if ( nSeasonId == null )
    {
      try
      {
        season = getDao().findCurrentSeason();
      }
      catch ( GDataException ex )
      {
        throw new VHServerException( "There is currently no active season.", ex );
      }
    }
    else
      season = getDao().findEntity( GSeason.class, nSeasonId );

    List<GENRStandings> list = getDao().findENRStandings( season );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public PagingLoadResult<VHJsData> getECRStandings( PagingLoadConfig cfg, String strLocale, Integer nSeasonId,
      String strAssocId ) throws VHServerException
  {
    GSortDir sortDir = GSortDir.DEFAULT;

    if ( cfg.getSortDir() != null && cfg.getSortDir() != SortDir.NONE )
      sortDir = GSortDir.valueOf( cfg.getSortDir().name() );

    GSeason season = null;
    GNationalAssociation assoc = null;

    if ( nSeasonId == null )
    {
      try
      {
        season = getDao().findCurrentSeason();
      }
      catch ( GDataException ex )
      {
        throw new VHServerException( "There is currently no active season.", ex );
      }
    }
    else
      season = getDao().findEntity( GSeason.class, nSeasonId );

    if ( strAssocId != null )
      assoc = getDao().findEntity( GNationalAssociation.class, strAssocId );

    List<GECRStandings> list = getDao().findECRStandings( season, assoc, cfg.getSortField(), sortDir, cfg.getOffset(),
        cfg.getLimit() );

    return new BasePagingLoadResult<VHJsData>( getDaoConverter().convert( list, resolveLocale( strLocale ) ),
        cfg.getOffset(), (int) getDao().findNumECRRankings( season, assoc ) );
  }

  public List<VHJsData> getHistoricStandings( String strLocale, String strElementId, Integer nSeasonId,
      VHJsStandingsType standingsType ) throws VHServerException
  {
    GSeason season;
    GContestElement element = getDao().findEntity( GContestElement.class, strElementId );
    GStandingsType type = GStandingsType.valueOf( standingsType.name() );

    if ( nSeasonId != null )
      season = getDao().findEntity( GSeason.class, nSeasonId );
    else
    {
      try
      {
        season = getDao().findCurrentSeason();
      }
      catch ( GDataException ex )
      {
        throw new VHServerException( "There is currently no active season.", ex );
      }
    }

    List<GArchivedStandings> list = getDao().findArchivedStandings( GArchivedStandingsType.END_OF_SEASON, season,
        element, type );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getInternationalContests( String strLocale ) throws VHServerException
  {
    GInternationalAssociation assoc = getDao().findEntity( GInternationalAssociation.class,
        GRulesConstants.ID_INTERNATIONAL_ASSOCIATION );

    return getDaoConverter().convert( assoc.getContests(), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getLeagues( String strLocale, String strAssocId ) throws VHServerException
  {
    List<GLeague> list = getDao().findLeaguesForAssociation( strAssocId );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getKOMatches( String strLocale, String strContestId, String strRoundId )
      throws VHServerException
  {
    GContestRound round;

    if ( strRoundId != null )
      round = getDao().findEntity( GContestRound.class, strRoundId );
    else
      round = getDao().findCurrentContestRound( getDao().findEntity( GContest.class, strContestId ) );

    // If there is no round, there is nothing to display! Let's return an empty list.
    //
    if ( round == null )
    {
      return new ArrayList<VHJsData>();
    }
    else if ( round.getFormat() == GContestRoundFormat.KNOCK_OUT )
    {
      GKO3ContestElement element = getDao().findEntity( GKO3ContestElement.class,
          round.getContestElements().get( 0 ).getId() );

      return getDaoConverter().convert( element.getKOMatches(), resolveLocale( strLocale ) );
    }
    else
      throw new VHServerException( "The round " + strRoundId + " of contest " + strContestId
          + " is not being played in KO format." );
  }

  public List<VHJsData> getMatchdays( String strLocale, String strElementId ) throws VHServerException
  {
    GRoundRobinContestElement element = getDao().findEntity( GRoundRobinContestElement.class, strElementId );

    List<GMatchday> list = getDao().findMatchdays( element );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getMatchesForClub( String strLocale, String strClubId ) throws VHServerException
  {
    GClub club = getDao().findEntity( GClub.class, strClubId );
    List<GMatch> list = getDao().findMatches( club, GMatchType.ALL );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getMatches( String strLocale, String strElementId ) throws VHServerException
  {
    GContestElement element = getDao().findEntity( GContestElement.class, strElementId );
    List<GMatch> list = getDao().findMatches( element );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getMatches( String strLocale, String strElementId, Integer nMatchdayNum )
      throws VHServerException
  {
    GContestElement element = getDao().findEntity( GContestElement.class, strElementId );
    GMatchday md;

    if ( nMatchdayNum == null )
      md = getDao().findLastPlayedMatchday( element );
    else
    {
      try
      {
        md = getDataValidator().validateMatchday( element, "nMatchdayNum", nMatchdayNum );
      }
      catch ( GDataValidationException ex )
      {
        throw new VHServerException( ex.getMessage(), ex );
      }
    }

    return getDaoConverter().convert( md.getMatches(), resolveLocale( strLocale ) );
  }

  public List<VHJsData> getSeasonsForClubHistory( String strLocale, String strContestId ) throws VHServerException
  {
    GContest contest = getDao().findEntity( GContest.class, strContestId );
    List<GSeason> list = getDao().findSeasonsForClubHistory( contest );

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getSeasonsForIntRankings( String strLocale ) throws VHServerException
  {
    List<GSeason> list = getDao().findSeasonsForIntRankings();

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }

  public List<VHJsData> getStandings( String strLocale, String strElementId, VHJsStandingsType standingsType,
      Integer nMatchday ) throws VHServerException
  {
    GStandingsType type = GStandingsType.valueOf( standingsType.name() );

    List<GStandings> list;

    try
    {
      GRoundRobinContestElement element = getDao().findEntity( GRoundRobinContestElement.class, strElementId );

      if ( nMatchday == null )
        list = getDao().findCurrentStandings( type, element );
      else
      {
        GMatchday matchday = getDataValidator().validateMatchday( element, "nMatchday", nMatchday );
        list = getDao().findStandings( matchday, type );
      }
    }
    catch ( GException ex )
    {
      throw new VHServerException( ex.getMessage(), ex );
    }

    return getDaoConverter().convert( list, resolveLocale( strLocale ) );
  }
}
